//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ReShade effect file
// Eye Adaption by brussell
//
// Credits:
// luluco250 - luminance get/store code from Magic Bloom
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

#include "ReShade.fxh"

//effect parameters
uniform float fAdp_Speed <
	ui_label = "AdaptionSpeed";
ui_tooltip = "Speed of adaption. The higher the faster";
ui_type = "slider";
ui_min = 0.0;
ui_max = 1.0;
ui_step = 0.001;
> = 0.1;

uniform bool bAdp_BrightenEnable <
	ui_label = "BrightenEnable";
ui_tooltip = "Enable Brightening";
> = true;

uniform float fAdp_BrightenThreshold <
	ui_label = "BrightenThreshold";
ui_tooltip = "A lower average screen luminance brightens the image";
ui_type = "slider";
ui_min = 0.0;
ui_max = 1.0;
ui_step = 0.001;
> = 0.2;

uniform float fAdp_BrightenMax <
	ui_label = "BrightenMax";
ui_tooltip = "Brightens the image by maximum value";
ui_type = "slider";
ui_min = 0.0;
ui_max = 1.0;
ui_step = 0.001;
> = 0.1;

uniform float fAdp_BrightenCurve <
	ui_label = "BrightenCurve";
ui_tooltip = "Brightening increase depending on average screen luminance. 1=linear growth, 0.5=quadratic, 2=sq. root";
ui_type = "slider";
ui_min = 0.2;
ui_max = 5.0;
ui_step = 0.001;
> = 1.0;

uniform float fAdp_BrightenDynamic <
	ui_label = "BrightenDynamic";
ui_tooltip = "Amount of pixel dependent brightening (less brightening of already bright pixels)";
ui_type = "slider";
ui_min = 0.0;
ui_max = 1.0;
ui_step = 0.001;
> = 0.5;

uniform float fAdp_BrightenBlack <
	ui_label = "BrightenBlack";
ui_tooltip = "Amount of lows preservation. 1=no black brightening";
ui_type = "slider";
ui_min = 0.0;
ui_max = 1.0;
ui_step = 0.001;
> = 0.5;

uniform float fAdp_BrightenSaturation <
	ui_label = "BrightenSaturation";
ui_tooltip = "Color saturation change while brightening.";
ui_type = "slider";
ui_min = -1.0;
ui_max = 1.0;
ui_step = 0.001;
> = 0.0;

uniform bool bAdp_DarkenEnable <
	ui_label = "DarkenEnable";
ui_tooltip = "Enable Darkening";
> = true;

uniform float fAdp_DarkenThreshold <
	ui_label = "DarkenThreshold";
ui_tooltip = "A higher average screen luminance darkens the image";
ui_type = "slider";
ui_min = 0.0;
ui_max = 1.0;
ui_step = 0.001;
> = 0.3;

uniform float fAdp_DarkenMax <
	ui_label = "DarkenMax";
ui_tooltip = "Darkens the image by maximum value";
ui_type = "slider";
ui_min = 0.0;
ui_max = 1.0;
ui_step = 0.001;
> = 0.4;

uniform float fAdp_DarkenCurve <
	ui_label = "DarkenCurve";
ui_tooltip = "Darkening increase depending on average screen luminance. 1=linear growth, 0.5=quadratic, 2=sq. root";
ui_type = "slider";
ui_min = 0.2;
ui_max = 5.0;
ui_step = 0.001;
> = 0.5;

uniform float fAdp_DarkenDynamic <
	ui_label = "DarkenDynamic";
ui_tooltip = "Amount of pixel dependent darkening (less darkening of already dark pixels)";
ui_type = "slider";
ui_min = 0.0;
ui_max = 1.0;
ui_step = 0.001;
> = 0.5;

uniform float fAdp_DarkenWhite <
	ui_label = "DarkenWhite";
ui_tooltip = "Amount of highs preservation. 1=no white darkening";
ui_type = "slider";
ui_min = 0.0;
ui_max = 1.0;
ui_step = 0.001;
> = 0.5;

uniform float fAdp_DarkenSaturation <
	ui_label = "DarkenSaturation";
ui_tooltip = "Color saturation change while darkening.";
ui_type = "slider";
ui_min = -1.0;
ui_max = 1.0;
ui_step = 0.001;
> = 0.0;

//global vars
#define LumCoeff float3(0.212656, 0.715158, 0.072186)
uniform float Frametime < source = "frametime"; > ;

//textures and samplers
texture2D texLuminance{ Width = 256; Height = 256; Format = R8; MipLevels = 7; };
texture2D texAvgLuminance{ Format = R16F; };
texture2D texAvgLuminanceLast{ Format = R16F; };

sampler SamplerLuminance{ Texture = texLuminance; };
sampler SamplerAvgLuminance{ Texture = texAvgLuminance; };
sampler SamplerAvgLuminanceLast{ Texture = texAvgLuminanceLast; };

//pixel shaders
float PS_Luminance(float4 pos : SV_Position, float2 texcoord : TEXCOORD) : SV_Target
{
   return dot(tex2D(ReShade::BackBuffer, texcoord.xy).xyz, LumCoeff);
}

float PS_AvgLuminance(float4 pos : SV_Position, float2 texcoord : TEXCOORD) : SV_Target
{
   float lum = tex2Dlod(SamplerLuminance, float4(0.5.xx, 0, 7)).x;
   float lumlast = tex2D(SamplerAvgLuminanceLast, 0.0).x;
   return lerp(lumlast, lum, fAdp_Speed * 10.0 / Frametime);
}

float PS_StoreAvgLuminance(float4 pos : SV_Position, float2 texcoord : TEXCOORD) : SV_Target
{
   return tex2D(SamplerAvgLuminance, 0.0).x;
}

float4 PS_Adaption(float4 pos : SV_Position, float2 texcoord : TEXCOORD) : SV_Target
{
	float4 color = tex2Dlod(ReShade::BackBuffer, float4 (texcoord.xy, 0, 0));
	static const float avglum = saturate(tex2D(SamplerAvgLuminance, 0.0).x);
	float adpcurve, adpdelta;
	float colorluma = dot(color.xyz, LumCoeff);
	float3 colorchroma = color.xyz - colorluma;

	[branch]
	if (bAdp_BrightenEnable == true && avglum < fAdp_BrightenThreshold) {
		adpcurve = (-fAdp_BrightenMax / pow(fAdp_BrightenThreshold, fAdp_BrightenCurve)) * pow(avglum, fAdp_BrightenCurve) + fAdp_BrightenMax;
		adpdelta = lerp(adpcurve, adpcurve - adpcurve * colorluma, fAdp_BrightenDynamic);
		adpdelta = lerp(adpdelta, min(colorluma, adpdelta), fAdp_BrightenBlack);
		colorluma += adpdelta;
		colorchroma = colorchroma * saturate(1.0 + (adpcurve / fAdp_BrightenMax) * fAdp_BrightenSaturation);
		color.xyz = colorluma + colorchroma;
	}
	[branch]
	if (bAdp_DarkenEnable == true && avglum > fAdp_DarkenThreshold) {
		adpcurve = (fAdp_DarkenMax / pow(1.0 - fAdp_DarkenThreshold, 1.0 / fAdp_DarkenCurve)) * pow(avglum - fAdp_DarkenThreshold, 1.0 / fAdp_DarkenCurve);
		adpdelta = lerp(adpcurve, adpcurve * colorluma, fAdp_DarkenDynamic);
		adpdelta = lerp(adpdelta, min(1.0 - colorluma, adpdelta), fAdp_DarkenWhite);
		colorluma -= adpdelta;
		colorchroma = colorchroma * saturate(1.0 + (adpcurve / fAdp_DarkenMax) * fAdp_DarkenSaturation);
		color.xyz = colorluma + colorchroma;
	}

	return color;
}

//techniques
technique EyeAdaption {

	pass Luminance
	{
		VertexShader = PostProcessVS;
		PixelShader = PS_Luminance;
		RenderTarget = texLuminance;
	}

	pass AvgLuminance
	{
		VertexShader = PostProcessVS;
		PixelShader = PS_AvgLuminance;
		RenderTarget = texAvgLuminance;
	}

	pass Adaption
	{
		VertexShader = PostProcessVS;
		PixelShader = PS_Adaption;
	}

	pass StoreAvgLuminance
	{
		VertexShader = PostProcessVS;
		PixelShader = PS_StoreAvgLuminance;
		RenderTarget = texAvgLuminanceLast;
	}
}